#Requires AutoHotkey v2.0

#Include g:\AHK\git-ahk-lib\lib\gdip\GdipStarter.ahk
#Include g:\AHK\git-ahk-lib\Extend.ahk
#Include ./ExternalLib.ahk

CoordMode 'Mouse'
CoordMode 'ToolTip'
CoordMode 'Pixel'

ScreenCap()

ScreenCap(isShowStatic := true) {
  ; helps to prevent mouse_hover and other events send to the window under the cursor,
  ; but also need to manually block mouse hotkeys
  block := Gui('-Caption +ToolWindow ')
  pBitmap := 0, hBitmap := 0
  if isShowStatic {
    pBitmap := Gdip_BitmapFromScreen()
    hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
    staticHdc := CreateCompatibleDC()
    staticObm := SelectObject(staticHdc, hBitmap)
    ; try draw a cursor on the current position,
    ; in fact, it needs to switch the states of cursor diaplay or hide
    Hotkey '``', (*) => CaptureCursor(staticHdc, 0, 0), 'On'
  }

  Screen_x := DllCall("GetSystemMetrics", "Int", 76)
  Screen_y := DllCall("GetSystemMetrics", "Int", 77)
  Screen_w := DllCall("GetSystemMetrics", "Int", 78)
  Screen_h := DllCall("GetSystemMetrics", "Int", 79)

  ; option E0x00080000 made it to be layered window,
  ; until the function UpdateLayeredWindow() is called, gui is invisible
  gui_ := Gui('-Caption +AlwaysOnTop +ToolWindow +E0x00080000')
  ; no need to specfic the size or position
  gui_.Show('NA')
  ; let it have independent of the device context
  WinSetExStyle("+0x20", gui_)

  pPen := Gdip_CreatePen(0xff00aeff, 2)
  pPenszx := Gdip_CreatePen(0x4400aeff, 1)
  pBrush := Gdip_BrushCreateSolid(0x8f000000)
  pPenbk := Gdip_CreatePen(0xffffffff, 1)

  o_mX := 0, o_mY := 0, cache := Map()
  while GetKeyState('LButton', 'p') = 0 { ; DOWN
    Sleep 20  ; Avoid explosive cpu growth
    try MouseGetPos(&n_mX, &n_mY, &c_id, &c_ctrl, 2)
    catch {
      continue
    }
    if c_id != block.Hwnd {
      if cache.Has(c_ctrl) {
        Deconstruction(cache.Get(c_ctrl), &c_win_x := 'x', &c_win_y := 'y', &c_win_w := 'w', &c_win_h := 'h')
      } else if cache.Has(c_id) {
        Deconstruction(cache.Get(c_id), &c_win_x := 'x', &c_win_y := 'y', &c_win_w := 'w', &c_win_h := 'h')
      } else {
        GetWindowRectWithoutShadow(c_id, &c_win_x, &c_win_y, &c_win_w, &c_win_h)
        key := c_id
        state := 'W'
        try {
          ControlGetPos(&c_ctrl_x, &c_ctrl_y, &c_ctrl_w, &c_ctrl_h, c_ctrl, 'ahk_id' c_id)
          c_win_w := c_ctrl_w, c_win_h := c_ctrl_h
          c_win_x += c_ctrl_x, c_win_y += c_ctrl_y
          key := c_ctrl
          state := 'C'
        }
        cache.Set(key, {
          x: c_win_x,
          y: c_win_y,
          w: c_win_w,
          h: c_win_h
        })
      }
    }
    if not (n_mX = o_mX && n_mY = o_mY) {
      ToolTip JoinStr(, c_win_x, c_win_y, c_win_w, c_win_h, state, WinGetTitle('ahk_id' c_id)), 0, 0
      o_mX := n_mX, o_mY := n_mY
      block.Hide()
      hbm := CreateDIBSection(Screen_w, Screen_h)
      hdc := CreateCompatibleDC()
      obm := SelectObject(hdc, hbm)
      G := Gdip_GraphicsFromHDC(hdc)
      Gdip_SetSmoothingMode(G, 4)

      if isShowStatic {
        BitBlt(hdc, 0, 0, Screen_w, Screen_h, staticHdc, 0, 0) ; display static background
        Gdip_FillRoundedRectangle(G, pBrush, 0, 0, Screen_w, Screen_h, 1)
        BitBlt(hdc, c_win_x, c_win_y, c_win_w, c_win_h, staticHdc, c_win_x, c_win_y) ; highlight area
        Gdip_DrawRoundedRectangle(G, pPen, c_win_x - 1, c_win_y - 1, c_win_w + 1, c_win_h + 1, 0)	; border

        DrawEnlargementfiFrame(G, pPenbk, pPenszx, staticHdc, hdc, Screen_w, Screen_h, n_mX, n_mY)
      } else {
        _hbm := CreateDIBSection(c_win_w, c_win_h)
        _hdc := CreateCompatibleDC()
        _obm := SelectObject(_hdc, _hbm)
        Gdip_FillRoundedRectangle(G, pBrush, 0, 0, Screen_w, Screen_h, 0) ; grey mask
        BitBlt(hdc, c_win_x, c_win_y, c_win_w, c_win_h, _hdc, 0, 0)		  ; make cur window visiable
        Gdip_DrawRoundedRectangle(G, pPen, c_win_x - 1, c_win_y - 1, c_win_w + 1, c_win_h + 1, 0)
        SelectObject(_hdc, _obm)
        DeleteObject(_hbm)
        DeleteDC(_hdc)
      }
    } else {
      block.Show('NA x' n_mX - 10 ' y' n_mY - 10 'w20 h20')
    }

    UpdateLayeredWindow(gui_.Hwnd, hdc, Screen_x, Screen_y, Screen_w, Screen_h)
    SelectObject(hdc, obm)
    DeleteObject(hbm)
    DeleteDC(hdc)
    Gdip_DeleteGraphics(G)

    if GetKeyState('RButton', 'p') = 1 or GetKeyState('Esc', 'p') = 1 {
      Clean()
      ExitApp()
    }

  }

  DrawEnlargementfiFrame(graphic, pPenbk, pPenszx, staticHdc, chdc, Screen_w, Screen_h, mx, my) {
    _hbm := CreateDIBSection(Screen_w, Screen_h)
    _hdc := CreateCompatibleDC()
    _obm := SelectObject(_hdc, _hbm)
    G := Gdip_GraphicsFromHDC(_hdc) 	; 光标放大框
    BitBlt(_hdc, 0, 0, Screen_w, Screen_h, staticHdc, 0, 0) ; 绘制放大框
    Gdip_DrawLine(G, pPenszx, mx, my - 12, mx, my + 12) ; 十字线
    Gdip_DrawLine(G, pPenszx, mx - 12, my, mx + 12, my)
    StretchBlt(chdc, mx + 15, my + 25, 126, 126, _hdc, mx - 10, my - 10, 21, 21) ; 放大框
    Gdip_DrawRoundedRectangle(graphic, pPenbk, mx + 14, my + 24, 127, 127, 0) ; 放大框轮廓

    SelectObject(_hdc, _obm)
    DeleteObject(_hbm)
    DeleteDC(_hdc)
    Gdip_DeleteGraphics(G)
  }

  Clean()
  ExitApp()

  Clean() {
    Tooltip
    Gdip_DeletePen(pPen)
    Gdip_DeleteBrush(pBrush)
    Gdip_DeletePen(pPenszx)
    Gdip_DeletePen(pPenbk)
    If isShowStatic {
      SelectObject(staticHdc, staticObm)
      DeleteDC(staticHdc)
      DeleteObject(hBitmap)
    }
  }

}

CaptureCursor(hDC, offsetX := 0, offsetY := 0) {
  mi := Buffer(24, 0)
  NumPut('uint', 24, mi, 0) ; cbSize
  DllCall('GetCursorInfo', 'uptr', mi.Ptr)
  bShow := NumGet(mi, 4, 'uint')
  hCursor := NumGet(mi, 8, 'uint')  ; 8 size
  xCursor := NumGet(mi, 16, 'uint')
  yCursor := NumGet(mi, 20, 'uint')

  If bShow && hCursor := DllCall("CopyIcon", "Uint", hCursor) {
    buf := Buffer(40, 0)  ; if just set it to 20, bug raise
    DllCall('GetIconInfo', 'uint', hCursor, 'uptr', buf.Ptr)
    xHotspot := NumGet(buf, 4, 'uint')
    yHotspot := NumGet(buf, 8, 'uint')
    hbmMask := NumGet(buf, 12, 'uint')
    hbmColor := NumGet(buf, 16, 'uint')
    DllCall("DrawIcon"
      , "Uint", hDC
      , "int", xCursor - xHotspot - offsetX
      , "int", yCursor - yHotspot - offsetY
      , "Uint", hCursor)

    DllCall("DestroyIcon", "Uint", hCursor)
    DllCall("DeleteObject", "Uint", hbmMask)
    DllCall("DeleteObject", "Uint", hbmColor)
  }
}